#include "SceneManager.h"
#include "AbstractScene.h"

//*************************
//********Scenes***********
//*************************
#ifdef LOAD_TEST_SCENE
#include "TestScene.h"
#include "SponzaScene.h"
#endif

//Include the demonstration scenes
#include "HouseDemoScene.h"
#include "HouseDispDemoScene.h"
#include "HouseEnvDemoScene.h"
#include "HousePOMDemoScene.h"

//And then the main demo scene.
#include "GameScene.h"
//*************************
//**End of Scenes headers**
//*************************

//Init static data
SceneManager* SceneManager::instance = NULL;

//Function which converts a std::string to wide string. 
std::wstring ToWideString(std::string s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}


SceneManager::SceneManager()
{
	//Init current scene pointer to NULL. 
	currentScene = NULL;

	//No current scene index. 
	currentSceneIndex = 0;
	 
	//Create the scenes in memory - we wont be calling the AllocateDynamicMemory()
	//function just yet. 
	//Also, sets the initial scene.
	LoadScenes();
}

SceneManager::~SceneManager()
{
	//Loop through our vector and delete everything. 
	//First, call deallocDynamicData - Granted, this could cause issues
	//if the user doesnt test for NULL (or set to NULL after deleting for that
	//matter) when deleting... but that is not my problem... Safe use of pointers FTW!

	//Use iterator for this task - first set it to point to the beginning of
	//the sceneVector.
	vector<AbstractScene*>::iterator it;
	it = sceneVector.begin();
	
	//Set current scene to NULL.
	currentScene = NULL;

	while (it != sceneVector.end())
	{
		//Since our vector is a vector of pointers, remeber we need
		//to derefence the itetator (which is a pointer to a pointer in
		//this case) before we can gain access to the actual object
		//using ->
		(*it)->CloseScene();
		delete (*it);
		//Increment the itterator. 
		it++;
	}

	//Clear vector of its pointers. 
	sceneVector.clear();

}



SceneManager* SceneManager::GetInstance()
{
	if (!instance)
		instance = new SceneManager();
	return instance;
}


void SceneManager::Release()
{
	if (instance)
	{
		delete instance;
		instance = NULL;
	}
}


template<class T>
void SceneManager::LoadNewSceneFromTemplate(wstring title)
{
	//Create an abstract scene pointer
	AbstractScene* newScene;
	
	//Create the scene of class type T in memory (new), set title and
	//push it on to the vector.
	newScene = new T();
	newScene->SetTitle(title);
	sceneVector.push_back(newScene);
}


void SceneManager::LoadScenes()
{
	//NOTE: Remember to #include the classes .h file above

	//We will load scenes in a clever format - Templated function!
	//LoadNewSceneFromTemplate<Class Identifier For Scene> (L"Scene Title");

	//**************************************************************************
	//********SCENE LOADING*****************************************************
	//**************************************************************************
	
	//Load test scene if requested.
#ifdef LOAD_TEST_SCENE
	LoadNewSceneFromTemplate<SponzaScene>(L"Sponza Scene");
#endif

	//Load demo scenes
	LoadNewSceneFromTemplate<HouseDemoSceneOne>(L"House Demo Scene One"); //Normal Mapping
	LoadNewSceneFromTemplate<HouseDispDemoScene>(L"House Demo Scene Two"); //Displacement mapping
	LoadNewSceneFromTemplate<HouseEnvDemoScene>(L"House Demo Scene Environment Map");
	LoadNewSceneFromTemplate<HousePOMDemoScene>(L"House Demo Scene Parallax Occlusion Mapping");

	//Load game scene (last as we open on the last scene)
	LoadNewSceneFromTemplate<GameScene>(L"Roo's World");

	//**************************************************************************
	//****END OF SCENE LOADING**************************************************
	//**************************************************************************

#ifdef OPEN_ON_LAST_SCENE
	//Setting current scene as our last scene.
	currentSceneIndex = sceneVector.size() - 1;
	currentScene = sceneVector[currentSceneIndex];
#else
	//Setting current scene to the first scene in the vector
	currentScene = sceneVector[0];
#endif

	//Allocate dynamic data for the current scene.
	currentScene->OpenScene();
}



wstring SceneManager::ReturnCurrentSceneTitle()
{
	if (currentScene)
		return (currentScene->ReturnSceneTitle());
	else
		return (L"D3D App");
}



void SceneManager::IncrementToNextScene()
{
	//Ensure currentScene exists. 
	assert(currentScene);

	//Deallocate dynamic data of the current scene
	currentScene->CloseScene();

	//Move to new scene and allocate data.
	//Increment currentSceneIndex and then check to see if it
	//is higher than (or equal) sceneVector.size(); If so, then set current
	//SceneIndex to 0 again (back to start)
	currentSceneIndex++;
	
	if (currentSceneIndex >= sceneVector.size())
		currentSceneIndex = 0;

	//Now set the current scene pointer and init dynamic data.
	currentScene = sceneVector[currentSceneIndex];
	currentScene->OpenScene();
}

void SceneManager::TransitionToSceneWithKey(std::string sceneToMoveTo, 
		bool shouldUnloadCurrentScene, bool shouldLoadNewScene)
{
	//Sleep for a bit
	Sleep(500);

	//Close the current scene
	if (shouldUnloadCurrentScene)
		currentScene->CloseScene();

	//Move to the new scene and call OpenScene to alloc and init data
	currentSceneIndex = FindSceneIndexForKey(sceneToMoveTo);

	//Set the current scene pointer
	currentScene = sceneVector[currentSceneIndex];
	
	//Call open scene to init data
	if (shouldLoadNewScene)
		currentScene->OpenScene();
	else
		currentScene->OnSceneReset();
}

unsigned SceneManager::FindSceneIndexForKey(std::string key)
{
	//Start at the first element in the vector - loop using itterators. 
	std::vector<AbstractScene*>::iterator it;
	it = sceneVector.begin();

	//Convert the passed in key to wide string. 
	std::wstring keyWS = ToWideString(key);

	//loop counter - init to 0.
	unsigned loopCounter = 0;

	//Loop through all scenes.
	while (it != sceneVector.end())
	{
		//Compare with this scenes (w)string. If a match has been found, then return the index
		//in the array (counted by loopCounter). 
		if (keyWS.compare((*it)->ReturnSceneTitle()) == 0)
			return loopCounter;

		//Next scene to test - also increment loop counter so we know where to index
		//in to the scenes vector
		it++;
		loopCounter++;
	}

	//If we reach here, no match has been found. Enter inf loop and
	//print error
	OutputDebugStringW(L"Scene Manager Error - Could not move to scene -> No scene with this key found in array!\n");
	while(1){}

	//Wont reach!
	return 0;
}

void SceneManager::ForwardOnMouseDownToCurrentScene(WPARAM btnState, int x, int y, UINT originalMessage)
{
	currentScene->OnMouseDown(btnState, x, y, originalMessage);
}



void SceneManager::ForwardOnMouseUpToCurrentScene(WPARAM btnState, int x, int y, UINT originalMessage)
{
	currentScene->OnMouseUp(btnState, x, y, originalMessage);
}



void SceneManager::ForwardOnMouseMoveToCurrentScene(WPARAM btnState, int x, int y)
{
	currentScene->OnMouseMove(btnState, x, y);
}



void SceneManager::ForwardOnResizeToCurrentScene()
{
	currentScene->OnResize();
}



void SceneManager::UpdateCurrentSceneWithDelta(float delta)
{
	if (currentScene)
		currentScene->UpdateWithDelta(delta);
}



void SceneManager::RenderCurrentScene()
{
	if (currentScene)
		currentScene->RenderScene();
}